package be.donovan.gui.helper.components;

import java.awt.Component;
import java.awt.Container;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.swingx.JXTable;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.ReflectionUtils;

import be.donovan.gui.helper.MainFrame;
import be.donovan.gui.helper.ServiceLocator;


/**
 * This abstract class extends abstractAction.
 * 
 * (1) The actionPerformed method generically acts on actionCommands by calling
 * a method named actionCommand + "Action" in this controller. So, if you have a
 * button with actionCommand "save", in order to bind it to this AbstractAction,
 * you should define a method named "saveAction that takes the ActionEvent as
 * parameter. The visibility of the method is of no importance.
 * 
 * You can find a complete example in the samples package.
 * 
 * @author usewils.f
 */
public abstract class AbstractController<T extends ViewI> extends
		AbstractAction {
	private ResourceBundleMessageSource messageSource = ServiceLocator.getService(ResourceBundleMessageSource.class);
	private boolean reactingOnOwnEvents = true;

	private static final Collection<String> actionRecorder = Collections
			.synchronizedCollection(new ArrayList<String>());

	protected Log logger = LogFactory.getLog(this.getClass());

	private T view;

	private static JFrame application;

	public void actionPerformed(final ActionEvent event) {
		try {
			final Object current = this;
			if (((Component) view).isVisible()) {
				String actionMethodName = event.getActionCommand() + "Action";
				final String beforeMethodName = "pre" + actionMethodName;
				final String actualMethodName = "on" + actionMethodName;
				final String afterMethodName = "post" + actionMethodName;
				invoke(beforeMethodName, current, event);
				invoke(actualMethodName, current, event);
				invoke(afterMethodName, current, event);
			}
		} catch (Throwable e) {
			JErrorDialog.showError((Component) getView(), e.getMessage(), e);
		}
	}

	private void invoke(String methodName, Object o, Object arg) {
		Method m = null;
		methodName = removeAllSpaces(methodName);
		m = ReflectionUtils.findMethod(o.getClass(), methodName,
				new Class[] { arg.getClass() });
		if (m != null) {
			actionRecorder.add(Calendar.getInstance().getTime() + ":"
					+ o.getClass() + "." + m.getName());
			m.setAccessible(true);
			ReflectionUtils.invokeMethod(m, o, new Object[] { arg });
		} else {
			if (logger.isDebugEnabled()) {
				logger
						.debug(methodName
								+ " does not exist. Check the spelling");
			}
		}
	}

	public String removeAllSpaces(String aString) {
		char tempChar = ' ';
		StringBuffer tempString = new StringBuffer(); // Holds the partly
		// blurred string

		for (int i = 0; i < aString.length(); i++) {
			try {
				tempChar = aString.charAt(i); // Read the first char
				// If the char is a space but not a newline
				if (!Character.isWhitespace(tempChar) && tempChar != '\n') {
					tempString.append(tempChar);
				}
			} catch (StringIndexOutOfBoundsException e) {
				System.err.println(e);
			}
		}
		return tempString.toString();
	}

	public void start() {
		try {
			view.afterPropertiesSet();
			if (view instanceof JPanel) {
				getMainFrame().add((Component) view);
			} else if (view instanceof MVCFrame) {
				((MVCFrame) view).setVisible(true);
			}
		} catch (Exception e) {
			JErrorDialog.showError("Foutje", e);
		}
	}

	protected JFrame getMainFrame() {
		if (application == null) {
			application = MainFrame.getInstance();
		}
		return application;
	}

	public void stop() {
		cleanup();
		if (view instanceof MVCFrame) {
			((MVCFrame) view).dispose();
			((MVCFrame) view).setVisible(false);
		} else if (view instanceof MVCPanel) {
			Container parent = (MVCPanel) view;
			parent.setVisible(false);
			while (!(parent instanceof JInternalFrame) && parent != null) {
				parent = parent.getParent();
			}
			if (parent != null) {
				((JInternalFrame) parent).dispose();
			}
		}
		saveJXTablePreferences();
		// CatalogRegistry.unregister(view);
	}
	private void saveJXTablePreferences() {
		for (JXTable table : JXTableRegistry.getTablesOf((ViewI)view)) {
			TableConfiguration.saveTableConfiguration(table, table.getName());
		}
	}

	public void cleanup() {
	}

	protected void beforeRefresh() {
	}

	public void doDeferredLayout() {
	}

	public void refresh() {
		cleanup();
		beforeRefresh();
		initData();
	}

	public abstract void initListeners();

	public abstract void initData();

	public T getView() {
		return view;
	}

	public void packWindow() {
		Object o = this.getView();
		if (o instanceof Window) {
			((Window) o).pack();
		} else if (o instanceof JComponent) {
			((Window) ((JComponent) o).getRootPane().getParent()).pack();
		}

	}

	@SuppressWarnings("unchecked")
	public void setView(ViewI view) {
		this.view = (T) view;
	}

	protected JButton setupActionListener(JButton button, String actionCommand) {
		button.setActionCommand(actionCommand);
		ActionListener[] actionListeners = button
				.getListeners(ActionListener.class);
		for (ActionListener listener : actionListeners) {
			button.removeActionListener(listener);
		}
		button.addActionListener(this);
		return button;
	}

	public JMenuItem setupActionListener(JMenuItem menuItem,
			String actionCommand) {
		menuItem.setActionCommand(actionCommand);
		ActionListener[] actionListeners = menuItem
				.getListeners(ActionListener.class);
		for (ActionListener listener : actionListeners) {
			menuItem.removeActionListener(listener);
		}
		menuItem.addActionListener(this);
		return menuItem;
	}

	protected JComboBox setupActionListener(JComboBox comboBox,
			String actionCommand) {
		comboBox.setActionCommand(actionCommand);
		ActionListener[] actionListeners = comboBox
				.getListeners(ActionListener.class);
		for (ActionListener listener : actionListeners) {
			comboBox.removeActionListener(listener);
		}
		comboBox.addActionListener(this);
		return comboBox;
	}

	public static Collection<String> getActionRecorder() {
		return actionRecorder;
	}

	public boolean isReactingOnOwnEvents() {
		return reactingOnOwnEvents;
	}

	public void setReactingOnOwnEvents(boolean reactingOnOwnEvents) {
		this.reactingOnOwnEvents = reactingOnOwnEvents;
	}
	public String getMessage(String string) {
		return messageSource.getMessage(string, null, string, null);
	}
	public JMenuItem createMenuItem(String string) {
		return setupActionListener(new JMenuItem(getMessage(string)), string);
	}
	public JButton createButton(String string){
		return setupActionListener(new JButton(getMessage(string)), string);
	}

}
